package org.example.statemachine.handler;

import org.example.statemachine.entity.Order;
import org.example.statemachine.enums.OrderStatus;
import org.example.statemachine.enums.OrderStatusChangeEvent;
import org.example.statemachine.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 订单状态机处理器 - 负责管理订单状态机的生命周期和状态转换
 * 通过Spring StateMachine实现订单状态的事件驱动管理，并持久化状态变更
 */
@Component
public class OrderStateMachineHandler {

    /**
     * 消息头键 - 用于在状态机事件中传递订单ID
     */
    private static final String ORDER_ID_HEADER = "orderId";

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StateMachineFactory<OrderStatus, OrderStatusChangeEvent> stateMachineFactory;

    /**
     * 状态机缓存 - 维护订单ID与状态机实例的映射关系
     * 使用ConcurrentHashMap确保线程安全，支持高并发场景下的状态机复用
     */
    private ConcurrentMap<Long, StateMachine<OrderStatus, OrderStatusChangeEvent>> stateMachineCache = new ConcurrentHashMap<>();

    /**
     * 处理订单状态变更事件
     *
     * @param orderId      订单ID
     * @param sourceStatus 当前订单状态（用于验证状态机上下文）
     * @param event        触发状态变更的事件
     * @return 状态转换是否成功
     */
    public boolean handleEventWithState(Long orderId, OrderStatus sourceStatus, OrderStatusChangeEvent event) {
        // 1. 从缓存获取或创建状态机实例，并重置到当前订单状态
        StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine = getOrCreateStateMachine(orderId, sourceStatus);

        // 2. 构建包含事件和订单ID的消息
        Message<OrderStatusChangeEvent> message = MessageBuilder
                .withPayload(event) // 事件类型（如PAYED、DELIVERY）
                .setHeader(ORDER_ID_HEADER, orderId) // 订单ID作为消息头
                .build();

        // 3. 发送事件到状态机，触发状态转换
        boolean result = stateMachine.sendEvent(message);

        // 4. 状态转换成功后，持久化最新状态到数据库
        if (result) {
            Order order = new Order();
            order.setId(orderId);
            // 获取状态机的当前状态并转换为数据库存储的整数值
            order.setStatus(stateMachine.getState().getId().getKey());
            // 更新数据库中的订单状态
            orderMapper.updateById(order);
        }

        return result;
    }

    /**
     * 获取或创建订单对应的状态机实例
     *
     * @param orderId 订单ID
     * @param status  订单当前状态
     * @return 状态机实例
     */
    private StateMachine<OrderStatus, OrderStatusChangeEvent> getOrCreateStateMachine(Long orderId, OrderStatus status) {
        // 使用computeIfAbsent确保原子性操作：不存在则创建，存在则直接返回
        return stateMachineCache.computeIfAbsent(orderId, k -> {
            // 1. 从工厂创建具有唯一ID的状态机实例
            StateMachine<OrderStatus, OrderStatusChangeEvent> machine = stateMachineFactory.getStateMachine(String.valueOf(orderId));

            // 2. 重置状态机到指定状态（从数据库读取的当前状态）
            // 先停止状态机以进入可配置状态
            machine.stop();
            // 使用状态访问器重置状态机上下文，确保与数据库状态一致
            machine.getStateMachineAccessor()
                    .doWithAllRegions(sma -> sma.resetStateMachine(
                            new DefaultStateMachineContext<>(status, null, null, null)));
            // 重新启动状态机，使其处于活动状态
            machine.start();

            return machine;
        });
    }
}